package p18_统一转为环绕通知;

import cn.hutool.core.collection.ListUtil;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import java.util.List;

/**
 * 调用链模拟实现
 * 责任链模式
 * 下面通知中，继承了MethodInterceptor都是环绕通知
 */
public class _2_MockMethodInvocation {

    public static void main(String[] args) throws Throwable {
        List<MethodInterceptor> methodInterceptors = ListUtil.toList(new Advice1(), new Advice2());
        Target target = new Target();
        MyInvocation invocation = new MyInvocation(target, target.getClass().getDeclaredMethod("bar"), new Object[0], methodInterceptors);
        invocation.proceed();
    }

    //一个环绕通知
    static class Advice1 implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("advice1 before...");
            Object result = invocation.proceed();
            System.out.println("advice1 after...");
            return result;
        }
    }
    //一个环绕通知
    static class Advice2 implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("advice2 before...");
            Object result = invocation.proceed();
            System.out.println("advice2 after...");
            return result;
        }
    }

    //调用链对象
    static class MyInvocation implements MethodInvocation {

        private Object target;  //目标对象
        private Method method;  //目标方法对象
        private Object[] args;  //目标方法参数
        List<MethodInterceptor> methodInterceptors;     //低级切面转换而来的环绕通知集合
        private int count = 1;  //递归次数

        public MyInvocation(Object target, Method method, Object[] args, List<MethodInterceptor> methodInterceptors) {
            this.target = target;
            this.method = method;
            this.args = args;
            this.methodInterceptors = methodInterceptors;
        }

        @Override
        public Method getMethod() {
            return method;
        }

        @Override
        public Object[] getArguments() {
            return args;
        }

        //在这里递归调用环绕通知
        @Override
        public Object proceed() throws Throwable {
            //根据递归次数，判断是否是否递归返回
            if (count > methodInterceptors.size()) {
                //满足条件时，说明环绕通知的前置通知调用完了，就调用目标方法，然后依次返回，也就是调用后置通知
                return method.invoke(target, args);
            }
            //依次调用每一个环绕通知，环绕通知里invocation.proceed()，实现递归调用
            MethodInterceptor methodInterceptor = methodInterceptors.get(count++ - 1);
            //每个MethodInterceptor的invoke又会调用到这个proceed方法，实现了递归(间接递归，没有直接自己调用自己)
            return methodInterceptor.invoke(this);
        }

        @Override
        public Object getThis() {
            return target;
        }

        @Override
        public AccessibleObject getStaticPart() {
            return method;
        }
    }
}
